package com.zzyl.serve.service.impl;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.huaweicloud.sdk.core.exception.ClientRequestException;
import com.huaweicloud.sdk.core.exception.ConnectionException;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.common.utils.bean.BeanUtils;
import com.zzyl.framework.config.IotClientConfig;
import com.zzyl.serve.dto.DeviceDto;
import com.zzyl.serve.vo.DeviceDetailVo;
import com.zzyl.serve.vo.DeviceReportDataVo;
import com.zzyl.serve.vo.ProductVo;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.zzyl.serve.mapper.DeviceMapper;
import com.zzyl.serve.domain.Device;
import com.zzyl.serve.service.IDeviceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;

import javax.annotation.Resource;

/**
 * 设备Service业务层处理
 *
 * @author liuyp
 * @date 2025-08-22
 */
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService
{
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private IoTDAClient client;
    @Resource
    private RedisTemplate<Object, Object> redisTemplate;


    /**
     * 查询设备
     *
     * @param id 设备主键
     * @return 设备
     */
    @Override
    public Device selectDeviceById(Long id)
    {
        return deviceMapper.selectById(id);
    }

    /**
     * 查询设备列表
     *
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<Device> selectDeviceList(Device device)
    {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增设备
     *
     * @param device 设备
     * @return 结果
     */
    @Override
    public int insertDevice(Device device)
    {
        return deviceMapper.insert(device);
    }

    /**
     * 修改设备
     *
     * @param device 设备
     * @return 结果
     */
    @Override
    public int updateDevice(Device device)
    {
        return deviceMapper.updateById(device);
    }

    /**
     * 批量删除设备
     *
     * @param ids 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Long[] ids)
    {
        return deviceMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除设备信息
     *
     * @param id 设备主键
     * @return 结果
     */


    @Override
    public int deleteDeviceById(Long id)
    {
        return deviceMapper.deleteById(id);
    }
    @ApiOperation("查询设备列表")
        //从物联网平台同步产品列表
    @Override
    public void syncProductLis() {
        // 创建查询产品列表的请求对象
        ListProductsRequest request =new ListProductsRequest();
        // 调用华为云IoTDA客户端的listProducts方法，获取产品列表响应
        ListProductsResponse response=client.listProducts(request);
        // 检查HTTP响应状态码是否为200(成功)，如果不是则抛出同步失败异常
        if(response.getHttpStatusCode()!=200){
            throw new BaseException("同步失败");
        }
        // 将获取到的产品列表数据转换为JSON字符串，并存储到Redis缓存中，
        // 使用CacheConstants.IOT_PRODUCT_LIST_KEY作为缓存键
        redisTemplate.opsForValue().set(CacheConstants.IOT_PRODUCT_LIST_KEY, JSONUtil.toJsonStr(response.getProducts()));
    }

    @ApiOperation("从物联网平台同步产品列表")
        @Override
    public List<ProductVo> allProduct() {
        // 从Redis缓存中获取产品列表的JSON字符串数据
        String str =(String) redisTemplate.opsForValue().get(CacheConstants.IOT_PRODUCT_LIST_KEY);
        // 判断从Redis中获取的字符串是否为空
        // 如果为空，说明缓存中没有产品数据，直接返回一个空的产品列表
        if(StringUtils.isEmpty(str)){
            return Collections.emptyList();
        }

        // JSONUtil.toList方法将JSON数组字符串转换为指定类型的对象列表
        return JSONUtil.toList(str, ProductVo.class);
    }

    @Override
    public void register(DeviceDto deviceDto) {
        // 使用Lambda表达式构建查询条件，检查设备名称是否已存在
        LambdaQueryWrapper<Device> wrapper = Wrappers.<Device>lambdaQuery()
                .eq(Device::getDeviceName, deviceDto.getDeviceName());
                // 如果数据库中已存在相同的设备名称，则抛出异常
                if(count(wrapper)>0){
                    throw new BaseException("设备名称已存在，请重新输入");
                }
                // 检查设备节点ID是否已存在
                Wrappers.<Device>lambdaQuery()
                        .eq(Device::getNodeId, deviceDto.getNodeId());
                // 如果数据库中已存在相同的节点ID，则抛出异常
                if(count(wrapper)>0){
                    throw new BaseException("设备ID已存在，请重新输入");
                }

                // 构建查询条件，检查是否已绑定同类型设备
                wrapper= Wrappers.<Device>lambdaQuery()
                        .eq(Device::getProductKey, deviceDto.getProductKey())
                        .eq(Device::getLocationType, deviceDto.getLocationType())
                        .eq(Device::getBindingLocation, deviceDto.getBindingLocation())
                        .eq(Device::getPhysicalLocationType, deviceDto.getPhysicalLocationType());
                // 如果已绑定同类型设备，则抛出异常
                if(count(wrapper)>0){
                    throw new BaseException("已绑定同类型设备");
                }

                // 创建添加设备的请求对象
                AddDeviceRequest request = new AddDeviceRequest();
                // 创建设备实体对象
                AddDevice body = new AddDevice();
                // 设置产品ID
                body.withProductId(deviceDto.getProductKey());
                // 设置设备名称
                body.withDeviceName(deviceDto.getDeviceName());
                // 设置设备节点ID
                body.withNodeId(deviceDto.getNodeId());

                // 创建认证信息对象
                AuthInfo authInfo = new AuthInfo();
                // 生成设备秘钥：利用Hutool工具包的UUID类生成
                String secret = UUID.randomUUID().toString(true);
                // 设置认证秘钥
                authInfo.withSecret(secret);
                // 将认证信息设置到设备对象中
                body.setAuthInfo(authInfo);
                // 将设备信息设置到请求对象中
                request.setBody(body);

                // 使用try-catch捕获华为云IoTDA平台可能返回的异常
                try {
                    // 调用华为云IoTDA客户端接口添加设备
                    AddDeviceResponse response = client.addDevice(request);

                    // 设备数据保存到数据库
                    // 使用BeanUtil工具进行属性拷贝，将DeviceDto转换为Device实体
                    Device device = BeanUtil.toBean(deviceDto, Device.class);
                    // 设置设备在华为云IoTDA平台的ID
                    device.setIotId(response.getDeviceId());
                    // 设置设备秘钥
                    device.setSecret(secret);

                    // 在数据库中新增设备记录
                    save(device);
                } catch (ClientRequestException e) {
                    // 如果是设备名称重复的错误，则抛出自定义异常提示
                    if (e.getErrorCode() != null && e.getErrorCode().equals("Invalid input. Duplicated deviceName.")) {
                        throw new BaseException("设备名称在华为云IoTDA平台已存在，请重新输入");
                    }
                    // 其他异常继续抛出
                    throw e;
                }
    }

    @ApiOperation("查询设备详细数据")
    @Override
    public DeviceDetailVo getInfo(String iotId) {
        LambdaQueryWrapper<Device> wrapper = Wrappers.<Device>lambdaQuery()
                .eq(Device::getIotId, iotId);
        Device device =getOne(wrapper);
        if (device == null){
            return null;
        }
        //再从华为云IoTDA中查询设备信息
        ShowDeviceRequest request = new ShowDeviceRequest();
        request.setDeviceId(iotId);
        ShowDeviceResponse response = client.showDevice(request);

        //属性拷贝
        DeviceDetailVo vo = new DeviceDetailVo();
        BeanUtil.copyProperties(device, vo);
        vo.setBindingLocation(Long.parseLong(device.getBindingLocation()));
        vo.setDeviceStatus(response.getStatus());
        String activeTime = response.getActiveTime();
        if (StringUtils.isNotEmpty(activeTime)) {
        //将activeTime日期字符串值'2025-08-18T13:09:07.104Z'转换成LocalDateTime对象
            LocalDateTime dateTime = LocalDateTime.parse(activeTime, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"));
        //dateTime是0时区的时间，转换成北京东8区
            dateTime = dateTime.atZone(ZoneId.of("UTC")).withZoneSameInstant(ZoneId.of("Asia/Shanghai")).toLocalDateTime();
            vo.setActiveTime(dateTime);
        }

        return vo;

    }
    @ApiOperation("查看设备上报数据")
    @Override
    public List<DeviceReportDataVo> queryServiceProperties(String iotId) {
        // 创建查询设备影子数据的请求对象
        ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
        // 设置要查询的设备ID
        request.setDeviceId(iotId);
        // 调用华为云IoTDA客户端接口，获取设备影子数据
        ShowDeviceShadowResponse response = client.showDeviceShadow(request);
        // 从响应中获取设备影子数据列表
        List<DeviceShadowData> shadow = response.getShadow();
        // 检查影子数据是否存在，如果为空则抛出异常
        if (shadow == null || shadow.isEmpty()) {
            throw new BaseException("查询失败");
        }

        // 获取设备实际上报的数据属性，使用reported.properties而不是desired.properties
        // reported表示设备实际报告的状态，desired表示期望设备达到的状态
        JSONObject jsonObject = JSONUtil.parseObj(shadow.get(0).getReported().getProperties());

        // 获取设备上报数据的时间戳
        String eventTimeStr = shadow.get(0).getReported().getEventTime();
        // 检查上报时间是否为空，如果为空则抛出异常
        if (StringUtils.isEmpty(eventTimeStr)) {
            throw new BaseException("上报时间为空");
        }

        // 解析时间格式，根据华为云IoTDA平台返回的实际格式"yyyyMMdd'T'HHmmss'Z'"进行解析
        LocalDateTime reportTime = LocalDateTime.parse(eventTimeStr, DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
        // 将UTC时间转换为北京时间（东八区）
        LocalDateTime eventTime = reportTime.atZone(ZoneId.of("UTC")).withZoneSameInstant(ZoneId.of("Asia/Shanghai")).toLocalDateTime();

        // 创建结果列表用于存储处理后的设备数据
        List<DeviceReportDataVo> list = new ArrayList<>();
        // 遍历设备上报的所有属性
        jsonObject.forEach((prop, value) -> {
            // 为每个属性创建一个DeviceReportDataVo对象
            DeviceReportDataVo vo = new DeviceReportDataVo();
            // 设置属性标识符（功能点ID）
            vo.setFunctionId(prop);
            // 设置数据上报时间
            vo.setEventTime(eventTime);
            // 设置属性值
            vo.setValue(value);
            // 将处理好的数据添加到结果列表中
            list.add(vo);
        });

        // 返回处理后的设备上报数据列表
        return list;
    }

    @ApiOperation("修改数据")
    @Override
    public void updateDeviceDto(DeviceDto deviceDto) {
        UpdateDeviceRequest request = new UpdateDeviceRequest();
        request.withDeviceId(deviceDto.getIotId());  // 使用设备在IoT平台的ID
        UpdateDevice body = new UpdateDevice();
        AuthInfoWithoutSecret authInfoWithoutSecret =new AuthInfoWithoutSecret();
        authInfoWithoutSecret.withSecureAccess( true);
        body.withAuthInfo(authInfoWithoutSecret);
        body.withDeviceName(deviceDto.getDeviceName());  // 更新设备名称
        request.withBody(body);
        try {
            UpdateDeviceResponse response = client.updateDevice(request);
            System.out.println(response.toString());
        } catch (ConnectionException e) {
            e.printStackTrace();
        }
        //拷贝DeviceDto里的属性
        Device device = new Device();
        BeanUtils.copyProperties(deviceDto,device);
        updateById(device);
    }

    @ApiOperation("删除设备")
    @Override
    public void deleteById(String iotId) {
        // 先从数据库中删除设备记录
        LambdaQueryWrapper<Device> wrapper = Wrappers.<Device>lambdaQuery()
                .eq(Device::getIotId, iotId);
        remove(wrapper);
        
        // 再从华为云IoTDA平台删除设备
        DeleteDeviceRequest request = new DeleteDeviceRequest();
        request.withDeviceId(iotId);
        try {
            DeleteDeviceResponse response = client.deleteDevice(request);
            // 检查响应状态
            if (response.getHttpStatusCode() != 204 && response.getHttpStatusCode() != 404) {
                // 如果不是成功删除(204)或设备不存在(404)，则记录警告信息
                System.out.println("Warning: Failed to delete device from IoT platform. HTTP Status: " 
                    + response.getHttpStatusCode());
            }
        } catch (com.huaweicloud.sdk.core.exception.ServiceResponseException e) {
            // 特别处理设备不存在的情况(404)
            if (e.getHttpStatusCode() == 404) {
                System.out.println("Device does not exist on IoT platform, skipping deletion.");
            } else {
                // 其他异常仍然打印错误信息
                e.printStackTrace();
            }
        } catch (Exception e) {
            // 捕获其他可能的异常
            e.printStackTrace();
        }
    }

    // 查询设备物模型属性
    @Override
    public List<ServiceCapability> queryProductModelProperties(String productId) {
        // 创建查询产品详情的请求对象
        ShowProductRequest request = new ShowProductRequest();
        // 设置要查询的产品ID
        request.setProductId(productId);
        // 调用华为云IoTDA客户端的showProduct方法，获取产品详情响应
        ShowProductResponse response = client.showProduct(request);
        // 检查HTTP响应状态码是否为200(成功)
        if(response.getHttpStatusCode()!=200){
        // 如果响应状态码不是200，说明查询失败，抛出自定义异常
            throw new BaseException("查询信息失败");
        }
        // 如果查询成功，从响应中获取并返回服务功能列表
        return response.getServiceCapabilities();
    }


}
